#pragma once
#include <pthread.h>
#include <unistd.h>
#include <queue>

namespace ns_block_queue
{
    const int default_cap = 5;

    template<class T>
    class BlockQueue
    {
    private:
	std::queue<T> q; 
	int _cap; 
	pthread_cond_t _is_full;    //缓冲区是否满了
	pthread_cond_t _is_empty;   //缓冲区是否为空
	pthread_mutex_t _mtx;  
    private:
	void lock()
	{
	    pthread_mutex_lock(&_mtx);
	}
	
	void unlock()
	{
	    pthread_mutex_unlock(&_mtx);
	}

	void ProducterWait()
	{
	    pthread_cond_wait(&_is_empty, &_mtx);
	}
	
	void ConsumerWait()
	{
	    pthread_cond_wait(&_is_full, &_mtx);
	}

	void ProducterSignal()
	{
	    pthread_cond_signal(&_is_empty);
	}

	void ConsumerSignal()
	{
	    pthread_cond_signal(&_is_full);
	}
	
	bool IsFull()
	{
	    return q.size() == _cap;
	}

	bool IsEmpty()
	{
	    return q.empty();
	}
    public:
	BlockQueue(const int cap = default_cap) : _cap(cap)
	{
	    pthread_cond_init(&_is_full, nullptr);
	    pthread_cond_init(&_is_empty, nullptr);
	    pthread_mutex_init(&_mtx, nullptr);
	}

	~BlockQueue()
	{
	    pthread_cond_destroy(&_is_full);	
	    pthread_cond_destroy(&_is_empty);
	    pthread_mutex_destroy(&_mtx);
	}
    public:
	void push(const T& t)
	{
	    lock();
	    while(IsFull())
	    {
		ProducterWait();
	    }
	    q.push(t);
	    ConsumerSignal();
	    unlock();
	}
	
	void pop(T* out)
	{
	    lock();
	    while(IsEmpty())
	    {
		ConsumerWait();
	    }
	    *out = q.front();
	    q.pop();
	    ProducterSignal();
	    unlock();
	}
    };
}
